#pragma once
#pragma warning(push)
#pragma warning(disable:4267)

#include <boost/asio.hpp>
#include <string>
#include <vector>
#include <set>
#include <boost/thread.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include "connection.hpp"
#include <boost/bind.hpp>

namespace commontcp {
    namespace server {
        /// The top-level class
        template<class t_session_handler>
        class server
            : private boost::noncopyable
        {
            /// The next connection to be accepted.  
            typedef boost::shared_ptr< connection<t_session_handler> > CONN_PTR;
        public:
            /// Construct the server to listen on the specified TCP address and port, and
            /// serve up files from the given directory.
            //explicit server(const std::string& address, const std::string& port, t_session_handler *thehandler);
            server(const std::string& address, const std::string& port, boost::shared_ptr<t_session_handler> thehandler)
                : thread_pool_size_(4),
                signals_(io_service_),
                acceptor_(io_service_),
                session_handler_(thehandler),
                io_service_outsize_(NULL),
                multiple_mode_(false)
            {
                // Register to handle the signals that indicate when the server should exit.
                // It is safe to register for the same signal multiple times in a program,
                // provided all registration for the specified signal is made through Asio.
                signals_.add(SIGINT);
                signals_.add(SIGTERM);
#if defined(SIGQUIT)
                signals_.add(SIGQUIT);
#endif // defined(SIGQUIT)
                signals_.async_wait(boost::bind(&server<t_session_handler>::handle_stop, this));

                // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
                boost::asio::ip::tcp::resolver resolver(io_service_);
                boost::asio::ip::tcp::resolver::query query(address, port);
                boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
                acceptor_.open(endpoint.protocol());
                acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
                acceptor_.bind(endpoint);
                acceptor_.listen();

                start_accept();
            }
            server(const std::string& address, const std::string& port, boost::shared_ptr<t_session_handler> thehandler, boost::asio::io_service &_io_service)
                : thread_pool_size_(4),
                signals_(io_service_),
                acceptor_(_io_service),
                session_handler_(thehandler),
                multiple_mode_(true),
                io_service_outsize_(_io_service)
            {
                // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
                boost::asio::ip::tcp::resolver resolver(_io_service);
                boost::asio::ip::tcp::resolver::query query(address, port);
                boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
                acceptor_.open(endpoint.protocol());
                acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
                acceptor_.bind(endpoint);
                acceptor_.listen();

                start_accept();
            }
            ~server()
            {

            }

            /// Run the server's io_service loop.
            void run()
            {
                if (multiple_mode_)
                {
                    return;
                }
                // Create a pool of threads to run all of the io_services.
                std::vector<boost::shared_ptr<boost::thread> > threads;
                for (std::size_t i = 0; i < thread_pool_size_; ++i)
                {
                    boost::shared_ptr<boost::thread> thread(new boost::thread(
                        boost::bind(&boost::asio::io_service::run, &io_service_)));
                    threads.push_back(thread);
                }

                // Wait for all threads in the pool to exit.
                for (std::size_t i = 0; i < threads.size(); ++i)
                    threads[i]->join();
            }

            /// 
            bool free_connection(CONN_PTR _ptr)
            {
                boost::recursive_mutex::scoped_lock lock(container_mutex_);
                typename std::set<CONN_PTR>::iterator iterfind = active_connections_.find(_ptr);
                if (active_connections_.end()!=iterfind)
                {
                    active_connections_.erase(iterfind);
                    free_connections_.push_back(_ptr);                                        
                    if (active_connections_.size()==MAX_ALLOW_CONNECTION-1)
                    {              
                        // look at get_free_connection() 
                        // if (current_size==MAX_ALLOW_CONNECTION)
                        // start_accept() will not start an async_accept
                        // we must start one if a connection is being freeed
                        start_accept();
                    }
                    return true;
                }
                return false;
            }
        private:
            /// 
            CONN_PTR get_free_connection()
            {
                boost::recursive_mutex::scoped_lock lock(container_mutex_);
                CONN_PTR _ptr;
                if (free_connections_.empty())
                {
                    size_t current_size = active_connections_.size();
                    if (current_size==MAX_ALLOW_CONNECTION)
                    {

                    }
                    else
                    {
                        if (multiple_mode_)
                        {
                            _ptr.reset(new connection<t_session_handler>(io_service_outsize_, session_handler_, this, current_size));
                        }
                        else
                        {
                            _ptr.reset(new connection<t_session_handler>(io_service_, session_handler_, this, current_size));
                        }
                        active_connections_.insert(_ptr);
                    }
                }
                else
                {
                    _ptr = free_connections_.back();
                    free_connections_.pop_back();
                }
                active_connections_.insert(_ptr);
                return _ptr;
            }
                        
            /// Initiate an asynchronous accept operation.
            void start_accept()
            {
                CONN_PTR _ptr = get_free_connection();
                if (_ptr.get())
                {
                    acceptor_.async_accept(_ptr->socket(),
                        boost::bind(&server<t_session_handler>::handle_accept, this,
                        boost::asio::placeholders::error, _ptr));
                }
            }

            /// Handle completion of an asynchronous accept operation.
            void handle_accept(const boost::system::error_code& e, CONN_PTR _ptr)
            {
                std::string str = e.message();
                if (!e)
                {
                    _ptr->start();
                }
                start_accept();
            }

            /// Handle a request to stop the server.
            void handle_stop()
            {
                io_service_.stop();
            }

            /// The number of threads that will call io_service::run().
            std::size_t thread_pool_size_;

            /// The io_service used to perform asynchronous operations.
            boost::asio::io_service io_service_;

            /// The signal_set is used to register for process termination notifications.
            boost::asio::signal_set signals_;

            /// Acceptor used to listen for incoming connections.
            boost::asio::ip::tcp::acceptor acceptor_;

            /// the whole connections container
            std::vector<CONN_PTR>   free_connections_;
            std::set<CONN_PTR>      active_connections_;
            boost::recursive_mutex  container_mutex_;

            /// The handler for all incoming packet
            boost::shared_ptr<t_session_handler> session_handler_;

            /// in mutiple mode, can not control the state of the server
            /// if you wanna stop , just destroy the object
            const bool              multiple_mode_;            
            boost::asio::io_service &io_service_outsize_;
        };

    } // namespace server
} // namespace commontcp

#pragma warning(pop)
